/**
 * @Copyright ®2015 Sinosoft Co. Ltd. All rights reserved.<br/>
 * 项目名称 : 电子商务云服务平台
 * 创建日期 : 2016年6月28日
 * 修改历史 : 
 *     1. [2016年6月30日]修改文件 by chihouliang
 * 添加Javadoc注释，更新代码规范，静态工具类增加private构造函数
 */
package com.gavinwind.inspay.common.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 【反射工具类】
 * @Description: 提供类反射的常用方法实现
 * @author chenxin
 * @date 2016年4月19日 上午4:53:12 
 * @version 1.0
 */
public class ClassInvokeUtil {
    
    private ClassInvokeUtil() {}

    /**
     * 调用参数为空的类的指定方法
     * @Title: getMethodValue 
     * @Description: 调用参数为空的类的指定方法，将返回值返回
     * @param cObject  反射调用类对象
     * @param cMethodName  反射调用类对象的指定方法
     * @return  返回该方法调用后的返回值
     * @throws Exception 系统异常
     */
    public static Object getMethodValue(Object cObject, String cMethodName) throws Exception {
        cMethodName = "get" + upperFirst(cMethodName);
        Method tMethod = getTargetMethod(cObject, cMethodName);
        Object[] tParamArr = null;
        return tMethod.invoke(cObject, tParamArr);
    }

    /**
     * 为指定属性赋值
     * @Title: setMethodValue 
     * @Description: 通过反射，为指定属性set值。
     * @param cObject   反射调用对象
     * @param cAttributeName   需要赋值的属性名
     * @param cValue     为该属性需要赋值的具体值
     * @throws Exception    系统异常
     */
    public static void setMethodValue(Object cObject, String cAttributeName, Object cValue) throws Exception {
        cAttributeName = "set" + upperFirst(cAttributeName);
        Method tMethod = getTargetMethod(cObject, cAttributeName);
        Object[] tParamArr = { cValue };
        tMethod.invoke(cObject, tParamArr);
    }

    /**
     * 获得该对象的所有声明属性名，且把属性第一个字母转换为大写
     * @Title: getAllFields 
     * @Description: 获得该对象的所有声明属性名，属性名的首字母大写
     * @param cObject  获得属性名的对象
     * @return   该对象的属性名列表，所有属性第一个字母转换为大写
     */
    public static List<String> getAllFields(Object cObject) {
        List<String> tFiledList = new ArrayList<String>();
        Class<?> tClass = cObject.getClass();
        Field[] tFieldArr = tClass.getDeclaredFields();
        for (int i = 0; i < tFieldArr.length; i++) {
            Field tField = tFieldArr[i];
            tFiledList.add(upperFirst(tField.getName()));
        }
        return tFiledList;
    }

    /**
     * 获得方法的返回值类型
     * @Title: getReturnType 
     * @Description: 通过反射，获得方法的返回值类型
     * @param cObject    反射调用的对象
     * @param cMethodName   需要获得的方法名(属性名称)
     * @return    返回该方法返回值的对象class实例
     */
    public static Class<?> getReturnType(Object cObject, String cMethodName) {
        cMethodName = "get" + upperFirst(cMethodName);
        Method tMethod = getTargetMethod(cObject, cMethodName);
        return tMethod.getReturnType();
    }

    /**
     * 通过反射获得对象的方法反射类
     * @Title: getTargetMethod 
     * @Description: 通过反射获得对象的方法反射类，只获取参数个数为0或1的方法
     * @param cFacade   反射的对象
     * @param cFacadeMethod   需要获得方法名称
     * @return    返回该方法的反射实例
     */
    public static Method getTargetMethod(Object cFacade, String cFacadeMethod) {
        Class<?> tClasstype = cFacade.getClass();
        Method[] tMethodArr = tClasstype.getMethods();
        for (int i = 0; i < tMethodArr.length; i++) {
            if (cFacadeMethod.equals(tMethodArr[i].getName())) {
                Class<?>[] tParamTypeArr = tMethodArr[i].getParameterTypes();
                if (null == tParamTypeArr || 0 == tParamTypeArr.length || 1 == tParamTypeArr.length) {
                    return tMethodArr[i];
                } else {
                    // 只调用参数个数为0或者1的方法，其他忽略
                    continue;
                }
            }
        }
        throw new RuntimeException("Invalid target tMethod " + cFacadeMethod + " In Class" + cFacade);
    }

    /**
     * 将对象转换为map
     * @Title: initCommonDTO 
     * @Description: 将对象转换为map，将对象属性名为key，属性值为value，属性名首字母大写
     * @param cCommonDTO  需要转换的对象
     * @return 将对象转换完成的map
     * @throws Exception  系统异常
     */
    public static Map<String, Object> initCommonDTO(Object cCommonDTO) throws Exception {
        Map<String, Object> tRsMap = new HashMap<String, Object>();
//        @SuppressWarnings("rawtypes")
        Class<?> tDtoClass = cCommonDTO.getClass();
        Field[] tFieldArr = tDtoClass.getDeclaredFields();
        for (int i = 0; i < tFieldArr.length; i++) {
            Field tFiled = tFieldArr[i];
            String tFiledName = tFiled.getName();
            Object tValue = getMethodValue(cCommonDTO, upperFirst(tFiledName));
            tRsMap.put(tFiledName.toUpperCase(), tValue);
        }
        return tRsMap;
    }

    /**
     * 通过map为对象赋值
     * @Title: objectToMap 
     * @Description: 通过map为对象赋值，根据map的key与对象的属性名相同进行映射，逐一赋值
     * @param cTarget   需要赋值的对象
     * @param cSource   赋值信息的封装map
     * @throws Exception   系统异常
     */
    public static void objectToMap(Object cTarget, Map<String, Object> cSource) throws Exception {
        Set<String> tKeyset = cSource.keySet();
        Iterator<String> tIterator = tKeyset.iterator();
        while (tIterator.hasNext()) {
            String tKey = tIterator.next();
            setMethodValue(cTarget, tKey, cSource.get(tKey));
        }
    }

    /**
     * 字符串的首字母大写
     * @Title: upperFirst 
     * @Description: 将传入的字符串首字母大写
     * @param cName  需要处理的字符串
     * @return   首字母大写的结果字符串
     */
    private static String upperFirst(String cName) {
        return cName.substring(0, 1).toUpperCase() + cName.substring(1, cName.length());
    }

    /**
     * 方法功能描述
     * @Title: invokeService 
     * @param cObject
     * @param cMethodName
     * @param cRequestObj
     * @return
     * @throws Exception
     */
    public static Object invokeService(Object cObject, String cMethodName, Object cRequestObj) throws Exception {
        Method tMethod = getTargetMethod(cObject, cMethodName);
        Object[] tReqObjArr = { cRequestObj };
        if (cRequestObj == null) {
            Object[] tParamArr = null;
            return tMethod.invoke(cObject, tParamArr);
        } else {
            return tMethod.invoke(cObject, tReqObjArr);
        }

    }

    /**
     * 通过反射为对象设置属性值
     * @Title: reflectSetProperty 
     * @Description: 通过反射为对象设置属性值，通过调用属性名的set方法，进行赋值
     * @param cObject   反射调用的对象
     * @param cPropertyName   属性名称
     * @param cParam    属性需要赋值的对象
     * @throws Exception  系统异常
     */
    @SuppressWarnings("rawtypes")
    public static void reflectSetProperty(Object cObject, String cPropertyName, Object cParam) throws Exception {
        String tSetMethodName = "set" + cPropertyName.substring(0, 1).toUpperCase() + cPropertyName.substring(1);
        Field tField = cObject.getClass().getDeclaredField(cPropertyName);
        Class tTypeClz = tField.getType();
        Method tSetMethod = cObject.getClass().getDeclaredMethod(tSetMethodName, tTypeClz);
        tSetMethod.invoke(cObject, cParam);
    }

    /**
     * 通过反射获取对象属性值
     * @Title: reflectGetProperty 
     * @Description: 通过反射获取对象属性值，利用反射，调用对象的get方法，获得该属性值
     * @param cObject   反射调用的对象
     * @param cPropertyName 属性名称
     * @return 该对象的属性值
     * @throws Exception  系统异常
     */
    public static Object reflectGetProperty(Object cObject, String cPropertyName) throws Exception {
        String tGetMethodName = "get" + cPropertyName.substring(0, 1).toUpperCase() + cPropertyName.substring(1);
        Method tGetMethod = cObject.getClass().getDeclaredMethod(tGetMethodName);
        return tGetMethod.invoke(cObject);
    }

    /**
     * 用于判断value的类型是基本类型的包装类
     * @Title: isSimpleTypeForMapValue 
     * @Description: 用于value的类型是基本类型的包装类
     * @param cClass  需要判断的类对象class
     * @return  是否为基本类型 true为是基本类型， false为不是基本类型
     * @throws Exception  系统异常
     */
    @SuppressWarnings("rawtypes")
    public static boolean isSimpleType(Class cClass) throws Exception {
        if (Boolean.class == cClass) {
            return true;
        } else if (Character.class == cClass) {
            return true;
        } else if (Short.class == cClass) {
            return true;
        } else if (Integer.class == cClass) {
            return true;
        } else if (Byte.class == cClass) {
            return true;
        } else if (Long.class == cClass) {
            return true;
        } else if (Double.class == cClass) {
            return true;
        } else if (Float.class == cClass) {
            return true;
        } else if (String.class == cClass) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 把source中的内容Copy到指定target
     * @param cSource 需要拷贝的对象
     * @param cTarget 目标对象
     */
    public static void copyProperties(Object cSource, Object cTarget) {
        copyProperties(cSource, cTarget, null);
    }

    /**
     * 把src中的内容Copy到指定target，但是忽略指定的属性
     * @param cSource 源对象
     * @param cTarget 目标对象
     * @param cIgnoreProperties 指定忽略的属性
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static void copyProperties(Object cSource, Object cTarget, String[] cIgnoreProperties) {
        if (null == cSource || null == cTarget) {
            return;
        }
        Set tExcludes = new HashSet();
        if (null != cIgnoreProperties) {
            tExcludes.addAll(Arrays.asList(cIgnoreProperties));
        }
        copyProperties(cSource, cTarget, tExcludes, false);
    }

    /**
     * 赋值对象属性
     * @param cSource 源对象
     * @param cTarget 目标对象
     * @param cProperties 参数
     * @param cIncluded 参数
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    private static void copyProperties(Object cSource, Object cTarget, Set cProperties, boolean cIncluded) {
        try {
            BeanInfo tSourceInfo = Introspector.getBeanInfo(cSource.getClass());
            Map tTargetDescrs = new HashMap();
            // init
            {
                BeanInfo tTargetInfo = Introspector.getBeanInfo(cTarget.getClass());
                PropertyDescriptor[] tTargetPds = tTargetInfo.getPropertyDescriptors();
                for (int i = 0; i < tTargetPds.length; i++) {
                    tTargetDescrs.put(tTargetPds[i].getName(), tTargetPds[i]);
                }
            }
            PropertyDescriptor[] tPds = tSourceInfo.getPropertyDescriptors();
            Object[] tParamArr = new Object[1];

            // collect some stat info for debug later...

            for (int i = 0; i < tPds.length; i++) {
                String tProperty = tPds[i].getName();
                if (cIncluded) {
                    if (!cProperties.contains(tProperty)) {
                        continue;
                    }
                } else {
                    if (cProperties.contains(tProperty)) {
                        continue;
                    }
                }
                PropertyDescriptor tTargetPD = (PropertyDescriptor) tTargetDescrs.get(tProperty);
                if (null != tTargetPD) {
                    Method tReadMethod = tPds[i].getReadMethod();
                    Method tWriteMethod = tTargetPD.getWriteMethod();
                    if (null == tReadMethod || null == tReadMethod) {
                        // warning: no read or write
                    } else {
                        try {
                            Class<?>[] tClassArr = tReadMethod.getParameterTypes();
                            if (tClassArr == null || tClassArr.length != 1) {
                                continue;
                            }
                            Object[] tParam = null;
                            Object tPropSrc = tReadMethod.invoke(cSource, tParam);
                            tParamArr[0] = convertIfNeeded(tClassArr[0], tPropSrc, tPds[i].getName());
                            tWriteMethod.invoke(cTarget, tParamArr);
                        } catch (IllegalArgumentException e) {
                            // warning: failed read or write
                        } catch (IllegalAccessException e) {
                            // warning: failed read or write
                        } catch (InvocationTargetException e) {
                            // warning: failed read or write
                        }
                    }
                } else {
                    // warning: no target property
                }
            }
        } catch (IntrospectionException e) {
            handleEx(e);
        }
    }

    /**
     * 异常转换方法
     * @param cEx 异常
     */
    private static void handleEx(IntrospectionException cEx) {
        throw new RuntimeException("Access Error", cEx);
    }

    /**
     * 直接调用对象方法, 无视private/protected修饰符.
     * 
     * @throws java.lang.reflect.InvocationTargetException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     */
    public static Object invokeMethod(final Object cObject, final String cMethodName, final Class<?>[] cParameterTypes, final Object[] cParameters)
            throws Exception {
        Method tMethod = getDeclaredMethod(cObject, cMethodName, cParameterTypes);
        if (tMethod == null) {
            throw new IllegalArgumentException("Could not find tMethod [" + cMethodName + "] on target [" + cObject + "]");
        }
        tMethod.setAccessible(true);
        return tMethod.invoke(cObject, cParameters);
    }

    /**
     * 内部使用方法
     * @param cType 参数
     * @param cValue 参数
     * @param cProperty 参数
     * @return Object 返回
     */
    private static Object convertIfNeeded(Class<?> cType, Object cValue, String cProperty) {
        if (null != cValue) {
            if (cValue instanceof Number) {
                return convertNumber(cType, (Number) cValue);
            }
            return cValue;
        }
        // 处理原始类型
        if (Integer.class == cType || Integer.TYPE == cType) {
            return new Integer(0);
        }
        if (Short.class == cType || Short.TYPE == cType) {
            return new Short((short) 0);
        }
        if (Long.class == cType || Long.TYPE == cType) {
            return new Long(0);
        }
        if (Float.class == cType || Float.TYPE == cType) {
            return new Float(0);
        }
        if (Double.class == cType || Double.TYPE == cType) {
            return new Double(0);
        }
        if (Byte.class == cType || Byte.TYPE == cType) {
            return new Integer(0);
        }
        if (Character.class == cType || Character.TYPE == cType) {
            return new Character('\0');
        }
        if (Boolean.class == cType || Boolean.TYPE == cType) {
            return Boolean.FALSE;
        }

        return cValue;
    }

    /**
     * 将number类型的对象转换为具体结果类型对象
     * @Title: convertNumber 
     * @Description: 将number类型的对象转换为具体子类对象
     * @param cDestType  需要转换的结果类型
     * @param cValue   number对象
     * @return
     */
    private static Number convertNumber(Class<?> cDestType, Number cValue) {
        if (cDestType == BigDecimal.class) {
            return new BigDecimal(cValue.toString());
        }
        if (cDestType == BigInteger.class) {
            return new BigInteger(cValue.toString());
        }
        if (cDestType == Long.class || cDestType == Long.TYPE) {
            return new Long(cValue.longValue());
        }
        if (cDestType == Integer.class || cDestType == Integer.TYPE) {
            return new Integer(cValue.intValue());
        }
        if (cDestType == Short.class || cDestType == Short.TYPE) {
            return new Short(cValue.shortValue());
        }
        if (cDestType == Float.class || cDestType == Float.TYPE) {
            return new Float(cValue.floatValue());
        }
        if (cDestType == Double.class || cDestType == Double.TYPE) {
            return new Double(cValue.doubleValue());
        }
        if (cDestType == Byte.class || cDestType == Byte.TYPE) {
            return new Byte(cValue.byteValue());
        }

        return cValue;
    }

    /**
     * 循环向上转型, 获取对象的DeclaredMethod.
     * 
     * 如向上转型到Object仍无法找到, 返回null.
     */
    protected static Method getDeclaredMethod(Object cObject, String cMethodName, Class<?>[] cParameterTypes) {
        if (cObject == null) {
            return null;
        }
        for (Class<?> tSuperClass = cObject.getClass(); tSuperClass != Object.class; tSuperClass = tSuperClass.getSuperclass()) {
            try {
                return tSuperClass.getDeclaredMethod(cMethodName, cParameterTypes);
            } catch (NoSuchMethodException e) {// NOSONAR
                // Method不在当前类定义,继续向上转型
            }
        }
        return null;
    }
}
